#include <stdlib.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include <png.h>
#include <time.h>
#include <string.h>
#include "adl.h"
#ifdef _OPENMP
    #include <omp.h>
#else
    #define omp_get_thread_num() 0
#endif

int numPart=0,imgIndex=0,imgQtd,sizeAux;
float progressValue=0.0, *timeTbl, sinTbl[360],cosTbl[360];
gboolean qtdAct = FALSE, padrON = TRUE, zoomIsOn=FALSE, firstRun=FALSE, canSave=FALSE;
GtkWidget *window, *qtdSpin, *qtdLabel, *configFrame, *adjQtd, *adjNum, *adjRaioLan, *adjRaioMorte, *radPadr, *radAvan, *dialog,
          *dimSpin, *adjProgress, *imag, *statusLabel, *nextBtn, *prevBtn, *codLabel, *frame, *zoomBtn, *saveBtn,*fileChoser;
GdkPixbuf *pixbuf;
//GError **error;
void fechar(GtkObject *object, gpointer user_data);
void ativarQtd();
void autoConfig();
void radTog();
void gerar();
void gerarText(unsigned int l, unsigned int numPartTarg, unsigned int numFrac, int incRaioLan, int incRaioMort);
void progressUp(int numFracAux, int numFrac);
void nextImg(gpointer *data);
void updImg(int index);
void updCodLabel();
void zoom();
void save();
void hideDialog();

int main (int argc, char *argv[])
{

    FILE *TblSin, *TblCos;
    if((TblSin=fopen("sinTbl.dat","rb"))==NULL)
        printf("\nTabela de seno não encontrada!\n");

    if((TblCos=fopen("cosTbl.dat","rb"))==NULL)
        printf("\nTabela de cosseno não encontrada!\n");

    fread(sinTbl, sizeof(float),360, TblSin);
    fread(cosTbl, sizeof(float),360, TblCos);

    fclose(TblSin);
    fclose(TblCos);

    if(argc==1){

        GtkBuilder *builder;

        gtk_init (&argc, &argv);

        builder = gtk_builder_new ();

        gtk_builder_add_from_file (builder, "FracGen3.glade", NULL);

        window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
        qtdSpin = GTK_WIDGET(gtk_builder_get_object(builder,"spinbutton5"));
        qtdLabel = GTK_WIDGET(gtk_builder_get_object(builder,"label5"));
        configFrame = GTK_WIDGET(gtk_builder_get_object(builder,"frame1"));
        adjQtd = GTK_WIDGET(gtk_builder_get_object(builder,"adjustment5"));
        adjNum = GTK_WIDGET(gtk_builder_get_object(builder,"adjustment2"));
        adjRaioLan = GTK_WIDGET(gtk_builder_get_object(builder,"adjustment3"));
        adjRaioMorte = GTK_WIDGET(gtk_builder_get_object(builder,"adjustment4"));
        radPadr = GTK_WIDGET(gtk_builder_get_object(builder,"radiobutton1"));
        radAvan = GTK_WIDGET(gtk_builder_get_object(builder,"radiobutton2"));
        dimSpin = GTK_WIDGET(gtk_builder_get_object(builder,"spinbutton2"));
        adjProgress = GTK_WIDGET(gtk_builder_get_object(builder,"adjustment6"));
        imag = GTK_WIDGET(gtk_builder_get_object(builder,"image2"));
        statusLabel = GTK_WIDGET(gtk_builder_get_object(builder,"label6"));
        nextBtn = GTK_WIDGET(gtk_builder_get_object(builder,"button3"));
        prevBtn = GTK_WIDGET(gtk_builder_get_object(builder,"button2"));
        codLabel = GTK_WIDGET(gtk_builder_get_object(builder,"label9"));
        frame = GTK_WIDGET(gtk_builder_get_object(builder,"aspectframe1"));
        zoomBtn = GTK_WIDGET(gtk_builder_get_object(builder,"button4"));
        saveBtn = GTK_WIDGET(gtk_builder_get_object(builder,"button5"));
        fileChoser = GTK_WIDGET(gtk_builder_get_object(builder,"filechooserbutton1"));
        dialog = GTK_WIDGET(gtk_builder_get_object(builder, "messagedialog1"));

        gtk_builder_connect_signals (builder, NULL);

        g_object_unref (G_OBJECT (builder));

        gtk_widget_set_name(GTK_WIDGET(nextBtn),"next");
        gtk_widget_set_name(GTK_WIDGET(prevBtn),"previus");
        gtk_widget_set_sensitive(qtdSpin,FALSE);
        gtk_widget_set_sensitive(qtdLabel,FALSE);
        gtk_widget_set_sensitive(configFrame,FALSE);

        //g_timeout_add(100,(GSourceFunc)progressUp,adjProgress);

        gtk_widget_show_all(window);
        gtk_main ();

    }

    if(argc == 4)
        gerarText(atoi(*(argv+1)),atoi(*(argv+2)),atoi(*(argv+3)),2,10);

    if(argc == 6)
        gerarText(atoi(*(argv+1)),atoi(*(argv+2)),atoi(*(argv+3)),atoi(*(argv+4)),atoi(*(argv+5)));

    else
        printf("\n------Help------\n\nParametros Basicos:\nFracGen(int DIMENSÃO, int NUMERO DE PARTICULAS, int QUANTIDADE DE IMAGENS)\nEx: ./FracGen 500 10000 2\n\nParametros Avançados:\nFracGen(int DIMENSÃO, int NUMERO DE PARTICULAS, int QUANTIDADE DE IMAGENS, int DISTANCIA DO RAIO DE LANÇAMENTO, int DISTANCIA DO RAIO DE MORTE)\nEx: ./FracGen 500 10000 2 2 10\n\nObs: Para rodar em modo grafico nao insira nenhum parametro!\n\n----------------\n");

    free(timeTbl);
    return 0;
}

void fechar(GtkObject *object, gpointer user_data)
{
    gtk_main_quit ();
}
void ativarQtd(){
    if(qtdAct == FALSE)
        qtdAct = TRUE;
    else{
        qtdAct = FALSE;
        gtk_adjustment_set_value(GTK_ADJUSTMENT(adjQtd),1);
    }
    gtk_widget_set_sensitive(qtdSpin,qtdAct);
    gtk_widget_set_sensitive(qtdLabel,qtdAct);
}

void autoConfig(){
        if(padrON==TRUE)
        {
            gint aux;
            aux = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(dimSpin));
            aux = (aux * aux) * 0.04;
            gtk_adjustment_set_value(GTK_ADJUSTMENT(adjNum),aux);
            gtk_adjustment_set_value(GTK_ADJUSTMENT(adjRaioLan),2);
            gtk_adjustment_set_value(GTK_ADJUSTMENT(adjRaioMorte),1);
        }
        updCodLabel();
}

void radTog(){
    if(gtk_widget_get_sensitive(configFrame)==TRUE){
        gtk_widget_set_sensitive(configFrame,FALSE);
        padrON=TRUE;
        autoConfig();
        updCodLabel();
    }
    else{
        gtk_widget_set_sensitive(configFrame,TRUE);
        padrON=FALSE;
    }
}

void updCodLabel(){
    char cod[64];
    snprintf(cod, sizeof(cod),"./FracGen %d %d %d %d %d",(int)gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(dimSpin)),(int)gtk_adjustment_get_value(GTK_ADJUSTMENT(adjNum)),(int)gtk_adjustment_get_value(GTK_ADJUSTMENT(adjQtd)),(int)gtk_adjustment_get_value(GTK_ADJUSTMENT(adjRaioLan)),(int)gtk_adjustment_get_value(GTK_ADJUSTMENT(adjRaioMorte)));
    gtk_label_set(GTK_LABEL(codLabel), cod);
}


void gerar(){
    unsigned int l,c,i,j,numPartTarg, raioAtual=0;
    char *matriz;
    int ** dist,numFracAux=0, numFrac;
    srand(time(NULL));
    char filename [16];

    zoomIsOn = FALSE;
    firstRun = TRUE;
    canSave = TRUE;

    snprintf(filename, sizeof(filename),"Fractal%d.png",numFracAux);
    while(remove(filename)==0){
        numFracAux++;
        snprintf(filename, sizeof(filename),"Fractal%d.png",numFracAux);
    }
    numFracAux=0;

    numFrac = gtk_adjustment_get_value(GTK_ADJUSTMENT(adjQtd));
    imgQtd = numFrac -1;

    free(timeTbl);
    timeTbl = (float *)malloc(sizeof(float)*numFrac);

    for(numFracAux=0;numFracAux<numFrac;numFracAux++){
        progressUp(numFracAux, numFrac);
        while(gtk_events_pending())
            gtk_main_iteration();
        //DEFININDO LINHAS COLUNAS E QTD DE PARTICULAS
        l=gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(dimSpin));
        c=l;
        sizeAux=l;
        numPartTarg = gtk_adjustment_get_value(GTK_ADJUSTMENT(adjNum));

        //ALOCANDO E PREENCHENDO MATRIZ
        matriz = matrizSetUp(l,c);

        //CRIANDO TABELA DE DISTANCIAS
        dist = malloc(l* sizeof(int*));
        if(dist == NULL)
        {
            printf("\n--ERRO DE ALOCAÇÃO EM dist!--");
            return;
        }

        for(i=0;i<l;i++)
        {
            dist[i] = malloc(c * sizeof(int));
            if(dist[i]==NULL)
            {
                printf("\n--ERRO DE ALOCAÇÃO EM dist!--");
                return;
            }
            for(j=0;j<c;j++)
            {
                dist[i][j]=-1;
            }
        }

        //UPDATE
        clock_t start = clock();
        enviarPartc(matriz, l, c,numPartTarg,&numPart,&raioAtual,dist,gtk_adjustment_get_value(GTK_ADJUSTMENT(adjRaioLan)),gtk_adjustment_get_value(GTK_ADJUSTMENT(adjRaioMorte)),sinTbl,cosTbl);
        *(timeTbl+numFracAux) = ((double)clock() - start ) / CLOCKS_PER_SEC;
        printf("\n--Fractal%d: Gerado em %.2f seg--\n",numFracAux, *(timeTbl+numFracAux));

        //GERANDO A IMAGEM
        snprintf(filename, sizeof(filename),"Fractal%d.png",numFracAux);
        FILE *fp;
        png_structp png_ptr;
        png_infop info_ptr;
        png_bytep row;
        char *title = "titulo";

        // Open file for writing (binary mode)
        fp = fopen(filename, "wb");
        if (fp == NULL) {
            fprintf(stderr, "\nO arquivo não pode ser abero!");
            return;
        }

        // Initialize write structure
        png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        if (png_ptr == NULL) {
            fprintf(stderr, "\nNao foi possivel alocar a write struct");
            return;
        }

        // Initialize info structure
        info_ptr = png_create_info_struct(png_ptr);
        if (info_ptr == NULL) {
            fprintf(stderr, "Nao foi possivel alocar a info struct\n");
            return;
        }

        // Setup Exception handling
        if (setjmp(png_jmpbuf(png_ptr))) {
            fprintf(stderr, "Erro durante a criação da png\n");
            return;
        }

        png_init_io(png_ptr, fp);

        // Write header (8 bit colour depth)
        png_set_IHDR(png_ptr, info_ptr, c, l,
                8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

        // Set title
        if (title != NULL) {
            png_text title_text;
            title_text.compression = PNG_TEXT_COMPRESSION_NONE;
            title_text.key = "Title";
            title_text.text = title;
            png_set_text(png_ptr, info_ptr, &title_text, 1);
        }
        png_write_info(png_ptr, info_ptr);

        // Write image data
        int y;
        for (y=0 ; y<l ; y++) {
            row = (png_bytep)charToGray(matriz,l,c,y,raioAtual,dist);
            png_write_row(png_ptr, row);
            free(row);
        }

        // End write
        png_write_end(png_ptr, NULL);

        if (fp != NULL) fclose(fp);
        if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
        if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);

        //LIBERANDO MEMORIA ALOCADA
        free(matriz);

        //LIBERANDO TABELA DE DISTANCIAS
        for(i=0;i<l;i++)
            {
               free(dist[i]);
            }
            free(dist);
        numPart =0;
        raioAtual=0;
    }
    // RE/CARREGANDO IMAGEM
    updImg(0);
    imgIndex =0;
    progressUp(numFracAux, numFrac);
}

void gerarText(unsigned int l, unsigned int numPartTarg, unsigned int numFrac,int incRaioLan, int incRaioMort){
    unsigned int c,i,j, raioAtual=0;
    c=l;
    char *matriz;
    int ** dist,numFracAux=0;
    srand(time(NULL));
    char filename [16];
    clock_t start;

    //REMOVENDO ARQUIVOS ANTIGOS
    snprintf(filename, sizeof(filename),"Fractal%d.png",numFracAux);
    while(remove(filename)==0){
        numFracAux++;
        snprintf(filename, sizeof(filename),"Fractal%d.png",numFracAux);
    }
    numFracAux=0;

    for(numFracAux=0;numFracAux<numFrac;numFracAux++){


        //ALOCANDO E PREENCHENDO MATRIZ
        matriz = matrizSetUp(l,c);

        //CRIANDO TABELA DE DISTANCIAS
        dist = malloc(l* sizeof(int*));
        if(dist == NULL)
        {
            printf("\n--ERRO DE ALOCAÇÃO EM dist!--");
            return;
        }

        for(i=0;i<l;i++)
        {
            dist[i] = malloc(c * sizeof(int));
            if(dist[i]==NULL)
            {
                printf("\n--ERRO DE ALOCAÇÃO EM dist!--");
                break;
            }
            for(j=0;j<c;j++)
            {
                dist[i][j]=-1;
            }
        }

        //UPDATE
        start= clock();
        enviarPartc(matriz, l, c,numPartTarg,&numPart,&raioAtual,dist, incRaioLan, incRaioMort,sinTbl,cosTbl);

        //GERANDO A IMAGEM
        printf("\n--Fractal%d: Gerado em %.2f seg--\n",numFracAux,((double)clock() - start ) / CLOCKS_PER_SEC);
        snprintf(filename, sizeof(filename),"Fractal%d.png",numFracAux);
        FILE *fp;
        png_structp png_ptr;
        png_infop info_ptr;
        png_bytep row;
        char *title = "titulo";

        // Open file for writing (binary mode)
        fp = fopen(filename, "wb");
        if (fp == NULL) {
            fprintf(stderr, "\nO arquivo não pode ser abero!");
            return;
        }

        // Initialize write structure
        png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        if (png_ptr == NULL) {
            fprintf(stderr, "\nNao foi possivel alocar a write struct");
            return;
        }

        // Initialize info structure
        info_ptr = png_create_info_struct(png_ptr);
        if (info_ptr == NULL) {
            fprintf(stderr, "Nao foi possivel alocar a info struct\n");
            return;
        }

        // Setup Exception handling
        if (setjmp(png_jmpbuf(png_ptr))) {
            fprintf(stderr, "Erro durante a criação da png\n");
            return;
        }

        png_init_io(png_ptr, fp);

        // Write header (8 bit colour depth)
        png_set_IHDR(png_ptr, info_ptr, c, l,
                8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

        // Set title
        if (title != NULL) {
            png_text title_text;
            title_text.compression = PNG_TEXT_COMPRESSION_NONE;
            title_text.key = "Title";
            title_text.text = title;
            png_set_text(png_ptr, info_ptr, &title_text, 1);
        }
        png_write_info(png_ptr, info_ptr);

        // Write image data
        int y;
        for (y=0 ; y<l ; y++) {
            row = (png_bytep)charToGray(matriz,l,c,y,raioAtual,dist);
            png_write_row(png_ptr, row);
            free(row);
        }

        // End write
        png_write_end(png_ptr, NULL);

        if (fp != NULL) fclose(fp);
        if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
        if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);

        //LIBERANDO MEMORIA ALOCADA
        free(matriz);

        //LIBERANDO TABELA DE DISTANCIAS
        for(i=0;i<l;i++)
            {
               free(dist[i]);
            }
            free(dist);
        numPart =0;
        raioAtual=0;
    }
}

void progressUp(int numFracAux, int numFrac)
{
    progressValue = (numFracAux*100)/numFrac;
    if(progressValue>100)
        progressValue=100;
    gtk_adjustment_set_value(GTK_ADJUSTMENT(adjProgress),progressValue);
}

void nextImg(gpointer *data)
{
    if((int)gtk_adjustment_get_value(GTK_ADJUSTMENT(adjQtd))>1){
        if(strcmp(gtk_widget_get_name(GTK_WIDGET(data)),"next") == 0)
        {
            imgIndex++;
            if(imgIndex>imgQtd)
                imgIndex = 0;
        }
        else
        {
            imgIndex--;
            if(imgIndex<0)
                imgIndex = imgQtd;
        }
        updImg(imgIndex);
        zoomIsOn = FALSE;
    }

}

void updImg(int index){
        char tit[32];
        snprintf(tit, sizeof(tit),"Fractal%d.png",index);
        gtk_image_set_from_file(GTK_IMAGE(imag),tit);
        snprintf(tit, sizeof(tit),"Fractal%d.png\n%.2f seg",index, *(timeTbl + index));
        gtk_label_set(GTK_LABEL(statusLabel), tit);
    }

void zoom(){
    if(firstRun == TRUE){
        if(zoomIsOn==FALSE){
            pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(imag));
            pixbuf = gdk_pixbuf_scale_simple(pixbuf,frame->allocation.width,frame->allocation.height,GDK_INTERP_BILINEAR);
            gtk_image_set_from_pixbuf(GTK_IMAGE(imag), pixbuf);
            zoomIsOn=TRUE;
        }
        else{
            updImg(imgIndex);
            zoomIsOn=FALSE;
        }
    }
}

void save(){
    if(canSave==TRUE){
        char *dir;
        char aux[512];
        char cmd [512];
        int a = time(NULL);
        dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(fileChoser));
        snprintf(aux, sizeof(aux),"Frc_Run_%d_%d",sizeAux,a);
        snprintf(cmd, sizeof(cmd),"cd %s; mkdir %s", dir, aux);
        system(cmd);
        snprintf(cmd, sizeof(cmd),"cp *.png %s/%s", dir, aux);
        system(cmd);
        snprintf(cmd, sizeof(cmd),"cd %s/%s; rm fragenIcon.png fragenLogo.png png_file.png", dir, aux);
        system(cmd);
        printf("\nSaved!");
        gtk_dialog_run(GTK_DIALOG (dialog));
        canSave=FALSE;
    }
}

void hideDialog(){
    gtk_widget_hide(GTK_WIDGET(dialog));
}
